/*
 * interface for scheme object.
 */

#ifndef __SCHEMEOBJECT_H
#define __SCHEMEOBJECT_H

#include "schemebase.h"

typedef enum SchemeType
{
	SCHEME_VOID,
	SCHEME_TYPE_FIRST = SCHEME_VOID,
	SCHEME_INT,
	SCHEME_CHAR,
	SCHEME_BOOL,
	SCHEME_STRING,
	SCHEME_NIL,
	SCHEME_PAIR,
	SCHEME_VECTOR,
	SCHEME_SYMBOL,
	SCHEME_CLOSURE,
	SCHEME_TYPE_LAST = SCHEME_CLOSURE,
 	SCHEME_TYPE_END
} SchemeType;

typedef struct SchemeIntData {
	int value ;
} SchemeIntData ;

typedef struct SchemeCharData {
	char value ;
} SchemeCharData ;

typedef struct SchemeBoolData {
	bool value ;
} SchemeBoolData ;

typedef struct SchemeStringData {
	Amount length ;
	string value ;
} SchemeStringData ;

typedef struct SchemePairData {
	struct SchemeObject* car ;
	struct SchemeObject* cdr ;
} SchemePairData ;

typedef struct SchemeVectorData {
	Amount length ;
	struct SchemeObject** vec ;
} SchemeVectorData ;

struct SymbolEntry ; // entry of a symbol in the top level
typedef struct SchemeSymbolData {
	struct SymbolEntry* entry;
} SchemeSymbolData ;

typedef struct SchemeClosureData {
	struct SchemeObject*** env ;
	MachineCode code ;
} SchemeClosureData ;

/*
 * value of a scheme object.
 * only one of them is selected, and the user (which is SchemeObject) is expected to
 * know which is the real one. note that the user usually allocates this structure in
 * the SIZE of the real data, so getting wrong at this means memory corruption.
 */
typedef union SchemeObjectData
{
  struct SchemeIntData intData;
  struct SchemeCharData charData;
  struct SchemeBoolData boolData;
  struct SchemeStringData stringData;
  struct SchemePairData pairData;
  struct SchemeVectorData vectorData;
  struct SchemeSymbolData symbolData;
  struct SchemeClosureData closureData;
} SchemeObjectData;

typedef struct SchemeObject
{
  SchemeType type ;
  SchemeObjectData* data ;
} SchemeObject;

// castings which are more syntactically comfortable
static inline void* Binary(void* x) { return (void*) x ; }
static inline SchemeObject* Sob(void* x) { return (SchemeObject*) x ; }
static inline SchemeObjectData* SobData(void* x) { return (SchemeObjectData*) x ; }

#define SOB_TYPE(x)	(Sob(x)->type)
#define SOB_DATA(x)	(Sob(x)->data)

static inline bool sobIsVoid(void* x) { return SOB_TYPE(x) == SCHEME_VOID ; }
static inline bool sobIsInt(void* x) { return SOB_TYPE(x) == SCHEME_INT ; }
static inline bool sobIsChar(void* x) { return SOB_TYPE(x) == SCHEME_CHAR ; }
static inline bool sobIsBool(void* x) { return SOB_TYPE(x) == SCHEME_BOOL ; }
static inline bool sobIsString(void* x) { return SOB_TYPE(x) == SCHEME_STRING ; }
static inline bool sobIsNil(void* x) { return SOB_TYPE(x) == SCHEME_NIL ; }
static inline bool sobIsPair(void* x) { return SOB_TYPE(x) == SCHEME_PAIR ; }
static inline bool sobIsVector(void* x) { return SOB_TYPE(x) == SCHEME_VECTOR ; }
static inline bool sobIsSymbol(void* x) { return SOB_TYPE(x) == SCHEME_SYMBOL ; }
static inline bool sobIsClosure(void* x) { return SOB_TYPE(x) == SCHEME_CLOSURE ; }
#define IS_SOB_TYPE(type, x) (sobType(x) == SCHEME_##type)

/*** ACCESSORS FOR CONVENIENCE ***/

#define SOB_INT_VALUE(x)			(SOB_DATA(x)->intData.value)
#define SOB_CHAR_VALUE(x)			(SOB_DATA(x)->charData.value)
#define SOB_BOOL_VALUE(x)			(SOB_DATA(x)->boolData.value)
#define SOB_STRING_LENGTH(x)		(SOB_DATA(x)->stringData.length)
#define SOB_STRING_VALUE(x)			(SOB_DATA(x)->stringData.value)
#define SOB_STRING_REF(x, i)		(SOB_STRING_VALUE(x)[i])
#define SOB_STRING_SET(x, i, c)		(SOB_STRING_VALUE(x)[i] = c)
#define SOB_PAIR_CAR(x)				(SOB_DATA(x)->pairData.car)
#define SOB_PAIR_CDR(x)				(SOB_DATA(x)->pairData.cdr)
#define SOB_VECTOR_LENGTH(x)		(SOB_DATA(x)->vectorData.length)
#define SOB_VECTOR_VALUE(x)			(SOB_DATA(x)->vectorData.vec)
#define SOB_VECTOR_REF(x, i)		(SOB_VECTOR_VALUE(x)[i])
#define SOB_VECTOR_SET(x, i, sob)	(SOB_VECTOR_VALUE(x)[i] = sob)
#define SOB_SYMBOL_ENTRY(x)			(SOB_DATA(x)->symbolData.entry)
#define SOB_SYMBOL_HAS_VALUE(x)		(SOB_SYMBOL_ENTRY(x)->isDefined)
#define SOB_SYMBOL_NAME(x)			(SOB_SYMBOL_ENTRY(x)->name)
#define SOB_SYMBOL_VALUE(x)			(SOB_SYMBOL_ENTRY(x)->sob)
#define SOB_CLOSURE_ENV(x)			(SOB_DATA(x)->closureData.env)
#define SOB_CLOSURE_CODE(x)			(SOB_DATA(x)->closureData.code)

/*** CONSTRUCTORS ***/

SchemeObject* makeSchemeObject(void) ;
SchemeObject* makeSchemeVoid(void) ;
SchemeObject* makeSchemeInt(int n) ;
SchemeObject* makeSchemeChar(char c) ;
SchemeObject* makeSchemeBool(bool b) ;
SchemeObject* makeSchemeString(string s) ;
SchemeObject* makeSchemeStringFromScratch(int length, char filler);
SchemeObject* makeSchemeNil(void);
SchemeObject* makeSchemePair(SchemeObject* car, SchemeObject* cdr);
SchemeObject* makeSchemeVector(int n, SchemeObject* init);
SchemeObject* makeSchemeSymbol(string name);
SchemeObject* makeSchemeClosure(struct SchemeObject*** env, MachineCode code) ;


string sobToString(SchemeObject* sob);
static inline bool sobIsFalse(void* x) { return sobIsBool(x) && SOB_BOOL_VALUE(x) == FALSE ; }
static inline bool sobIsntFalse(void* x) { return !sobIsBool(x) || SOB_BOOL_VALUE(x) != FALSE ; }

#endif /* __SCHEMEOBJECT_H */
